<?php

abstract class AbstractDAO {
	// logger
	protected $logger;
	
	// EntityManager
	protected $em;
	
	// Hier steht immer der aktuellste Fehler drin
	protected $error = null;
	
	// Hier muss der Name der zugrundeliegenden Modelklasse gespeichert werden
	protected $modelname;
	
	/**
	 * Der Konstruktor stattet die AbstractDAO mit einem Entitymanager und
	 * einem Logger aus.
	 */
	function __construct($modelname) {
		if (!isset($this->logger)) {
			$this->logger = Logger::getInstance();
		}
		
		$this->em = EntityManagerFactory::getInstance();
		
		$this->modelname = $modelname;
	}
	
	/**
	 * Dieser Destruktor gibt den logger und den Entitymanager wieder frei
	 */
	function __destruct() {
		unset($this->logger);
		unset($this->em);
	}
	
	/**
	 * Diese Funktion gibt ein array mit allen Entitäten der entsprechenden
	 * Modelklasse zurück.
	 * 
	 * @return numeric array 
	 */
	public function get_all() {
		// Wenn vorher ein Fehler aufgetreten ist, schlucken
		$this->handle_previous_error();
		
		// debug
		$this->logger->logFile("Loading all entities of ".$this->modelname." from Database...", LEVEL_DEBUG);
		
		// Lade alle Entitäten aus der DB
		$entities = $this->em->getRepository($this->modelname)->findAll();
		
		// debug
		$this->logger->logFile("Successfully loaded ". count($entities) . " ". $this->modelname . " entities from database", LEVEL_DEBUG);
		
		return $entities;
	}
	
	/**
	 * Diese Funktion liest die Entität mit der übergebenen ID aus der
	 * Datenban und gibt diese zurück
	 * 
	 * @param integer $id
	 */
	public function getById($id) {
		// Wenn vorher ein Fehler aufgetreten ist, schlucken
		$this->handle_previous_error();
		
		// debug
		$this->logger->logFile("Loading ".$this->modelname." with id '". $id ."' from database...", LEVEL_DEBUG);
		
		// Lade die User Entität mit der id aus der DB
		$entity = $this->em->find($this->modelname, $id);
		
		// debug
		$this->logger->logFile($entity ? "Loading ".$this->modelname." with id '". $id ."' was successfull." : "No ".$this->modelname." found with id '". $id ."'.", LEVEL_DEBUG);
		
		return $entity;
	}
	
	/**
	 * Diese Funktion prüft, ob es die Entität bereits in der Datenbank gibt
	 * und gibt einen boolean mit entsprechender aussage zurück.
	 * 
	 * @param AbstractModel $entity
	 */
	abstract public function exists($entity);
	
	/**
	 * Diese Funktion speichert die übergebene Entität in der Datenbank.
	 * 
	 * Dabei wird überprüft, ob es bereits eine Entität mit den selben
	 * Werten (abgesehen von der ID) in der Datebank gibt und das Speichern
	 * gegebenenfalls abgebrochen.
	 * 
	 * @param AbstractModel $entity
	 */
	public function save($entity) {
		// Wenn vorher ein Fehler aufgetreten ist, schlucken
		$this->handle_previous_error();
		
		// debug
		$this->logger->logFile("Saving ".$this->modelname." entity '".$entity->to_string()."' to the database...", LEVEL_DEBUG);
		
		// Prüfen, ob es die Entität schon gibt
		if ($this->exists($entity)) {
			$this->error = ENTITY_ALREADY_EXSISTS;
			
			// error
			$this->logger->logFile("The entity already exsists: '".$entity->to_string()."'", LEVEL_ERROR);
				
			return false;
		}
		
		// Speichere die Entität in der DB
		$this->em->persist($entity);
		$this->em->flush();
		
		// debug
		$this->logger->logFile($this->modelname." entity was successfully saved.", LEVEL_DEBUG);
		
		return true;
	}
	
	/**
	 * Lädt die Entität mit der übergebenen ID aus der Datenbank und gibt
	 * diese an die delete Funktion weiter, wodurch die Entität gelöscht 
	 * wird.
	 * 
	 * @param integer $id
	 */
	public function deleteById($id) {
		// Lade die Entität mit der übergebenen ID aus der DB und gebe diese
		// an delete weiter
		$this->delete($this->getById($id));
	}
	
	/**
	 * Über diese Funktion ist es möglich Änderungen forzunehmen, unmittelbar
	 * bevor die eigentliche Entität gelöscht wird.
	 * 
	 * @param AbstractModel $entity
	 */
	abstract function pre_delete($entity);
	
	/**
	 * Diese Funktion löscht die üergebene Entität aus der Datenbank
	 * 
	 * @param AbstractModel $entity
	 */
	public function delete($entity) {
		// Wenn vorher ein Fehler aufgetreten ist, schlucken
		$this->handle_previous_error();
		
		// debug
		$this->logger->logFile("Deleting ".$this->modelname." from database.", LEVEL_DEBUG);
		
		// pre-delete Methode
		$this->pre_delete($entity);
		
		// Lösche die Entität
		$this->em->remove($entity);
		$this->em->flush();
		
		// debug
		$this->logger->logFile($this->modelname." entity deleted.", LEVEL_DEBUG);
	}
	
	/**
	 * Gibt den error aus und leert den internen error cache
	 */
	public function get_error() {
		$err = $this->error;
		$this->error = null;
		return $err;
	}
	
	/**
	 * Behandelt vorhergegangene nicht ausgelesene errors
	 */
	protected function handle_previous_error() {
		// Falls vorher mal ein error aufgetreten ist, jedoch der
		// Fehler nicht ausgelesen wurde, wird dieser jetzt verworfen
		if ($pre_error = $this->get_error()) {
			// warn
			$this->logger->logFile("Previous error was not read: ". $pre_error, LEVEL_WARN);
		}
	}
}

?>